"""
/** Copyright 2020 Zhejiang Lab and Zhejiang University. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================
*/
"""
from utils import logRequest
import os
from distributed.descriptor.task_manager_tasks import Task as DesTask
from backend.settings import DATABASE_OFFICIAL_ACCOUNT
from task.models import serialize, unserialize, Task, TASK_OPTIONS, \
    GeneralTaskType, AlgorithmField, Algorithm
from server.models import Server
from my_models.models import Models, Share_Model
from my_datasets.models import Datasets
from rest_framework.views import APIView
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import parser_classes
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAdminUser
from rest_framework.parsers import FileUploadParser,MultiPartParser,JSONParser
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError, transaction
import re, datetime, time
from django.utils import timezone
import logging
from django.db.models import Q, F

logger_name = 'task.views'
logger = logging.getLogger(logger_name)

# options
class ReorgTaskOptions(APIView):
    @logRequest(logger_name)
    def get(self, request, format=None):
        user = request.user

        # Get dataset ids and names
        shared_file_query = Share_Model.objects.filter(user_id=user)
        shared_dataset_ids = shared_file_query.filter(file_type='d').values("file_id")
        datasets = Datasets.objects.filter(Q(id__in=shared_dataset_ids)|Q(user=user)|Q(is_public=True)).values("id", name=F('dataset_name'))
        logger.debug(datasets)

        # Get model ids and names
        shared_model_ids = shared_file_query.filter(file_type='m').values("file_id")
        models = Models.objects.filter(Q(id__in=shared_model_ids) | Q(user=user)|Q(is_public=True)).values("id", name=F('model_name'))
        logger.debug(models)
        options = {"datasets": list(datasets), "models": list(models)}

        # Get algorithms and fields
        algorithms = []
        algs = Algorithm.objects.all()
        for alg in algs:
            fields = AlgorithmField.objects.filter(alg=alg).values('field_name', 'field_value', 'field_note')
            algorithms.append({'id': alg.id, 'alg_name': alg.alg_name, 'fields': list(fields)})
        options['algorithms'] = algorithms

        for name, option_contents in TASK_OPTIONS.items():
            options[name] = [ {"id":k, "name":v}  for k, v in option_contents.items()]

        return Response(options, status=status.HTTP_200_OK)

class ReorgTasks(APIView):
    @logRequest(logger_name)
    def get(self, request, format=None):
        try:
            user = request.user
            tasks = Task.objects.filter(user=user)
            logger.debug(tasks)
            tasks = [t.summary for t in tasks]

        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(tasks, status=status.HTTP_200_OK)

    @logRequest(logger_name)
    def post(self, request, format=None):
        user = request.user
        task_info = request.data.get('task_info', None)
        logger.debug(task_info)
        if task_info is None:
            return Response({'error': 'Parameter errors'}, status=status.HTTP_400_BAD_REQUEST)

        # Preprocess task information
        task_name = GeneralTaskType.KNOWREORG.name
        if task_info.get("datasets", None) is None:
            task_info["datasets"] = None
        else:
            datasets = Datasets.objects.filter(id__in=task_info["datasets"])
            task_info["datasets"] = []
            for ds in datasets:
                if ds.user.username == DATABASE_OFFICIAL_ACCOUNT["username"]:
                    task_info["datasets"].append({"id": ds.id, "url_or_name": ds.dataset_name})
                else:
                    task_info["datasets"].append({"id": ds.id, "url_or_name": ds.url})

        for m_name in ('teacher_models', 'student_models'):
            if not task_info.get(m_name, []):
                task_info[m_name] = None
            else:
                models = Models.objects.filter(id__in=task_info[m_name]).values("id", "url", "model_name", username=F("user__username"))
                task_info[m_name] = []
                for m in models:
                    if m["username"] == DATABASE_OFFICIAL_ACCOUNT["username"]:
                        task_info[m_name].append({"id": m["id"], "url_or_name": m["model_name"]})
                    else:
                        task_info[m_name].append({"id": m["id"], "url_or_name": m["url"]})

        logger.debug(task_info)

        # Get a task instance and create a new record
        task = DesTask(task_name=task_name, **task_info)
        task_lb = serialize(task)
        Task.objects.create(task_uid=task.uuid, task_type=GeneralTaskType.KNOWREORG, task=task_lb, user=user)

        return Response({'response': 'success'}, status=status.HTTP_202_ACCEPTED)

    @logRequest(logger_name)
    def delete(self, request, format=None):
        user = request.user
        task_ids = [int(v) for v in request.query_params.getlist('task_ids[]', [])]
        logger.debug(task_ids)
        try:
            task = Task.objects.filter(id__in=task_ids, user=user).delete()
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'response': 'success'}, status=status.HTTP_200_OK)

class TaskDetails(APIView):
    @logRequest(logger_name)
    def get(self, request, format=None):
        user = request.user
        task_id = request.query_params.get('task_id', None)
        if not task_id:
            return Response({'error': "Invalid task_id"}, status=status.HTTP_400_BAD_REQUEST)
        task_id = int(task_id)
        logger.debug(task_id)
        try:
            task = Task.objects.get(user=user, id=task_id)
            details = task.details
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(details, status=status.HTTP_200_OK)

class AlgorithmInfo(APIView):
    permission_classes = (IsAdminUser, )
    @logRequest(logger_name)
    def get(self, request):
        res = []
        algs = Algorithm.objects.all()
        for alg in algs:
            alg_info = {'id': alg.id, 'alg_name': alg.alg_name}
            fields = AlgorithmField.objects.filter(alg=alg).values('field_name', 'field_value', 'field_note')
            alg_info['fields'] = list(fields)
            res.append(alg_info)
        return Response({'algorithms': res}, status=status.HTTP_200_OK)

    @logRequest(logger_name)
    def delete(self, request):
        alg_ids = request.query_params.getlist('algorithm_ids[]', [])
        try:
            Algorithm.objects.filter(id__in=alg_ids).delete()
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'response': 'success'}, status=status.HTTP_200_OK)

    @logRequest(logger_name)
    @transaction.atomic
    def post(self, request):
        alg_name = request.data.get('alg_name', None)
        fields = request.data.get('fields', None)
        if None in (alg_name, fields):
            return Response({'error': 'Parameter errors'}, status=status.HTTP_400_BAD_REQUEST)
        try:
            alg = Algorithm.objects.create(alg_name=alg_name)
            for field in fields:
                AlgorithmField.objects.create(alg=alg, **field)
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response({'response': 'success'}, status=status.HTTP_201_CREATED)

    @logRequest(logger_name)
    @transaction.atomic
    def put(self, request):
        alg_id = request.data.get('id', None)
        alg_name = request.data.get('alg_name', None)
        fields = request.data.get('fields', None)
        if None in (alg_id, alg_name, fields):
            return Response({'error': 'Parameter errors'}, status=status.HTTP_400_BAD_REQUEST)
        fields = {f['field_name']: [f['field_value'], f['field_note']] for f in fields}
        try:
            alg = Algorithm.objects.get(id=alg_id, alg_name=alg_name)
            old_fields = AlgorithmField.objects.filter(alg=alg)
            # update or delete old field
            for oldf in old_fields:
                new_v = fields.get(oldf.field_name, None)
                if new_v:
                    oldf.field_value = new_v[0]
                    oldf.field_note = new_v[1]
                    oldf.save()
                else:
                    oldf.delete()
                fields.pop(oldf.field_name, None)
            # add new field
            for k, v in fields.items():
                AlgorithmField.objects.create(alg=alg, field_name=k, field_value=v[0], field_note=v[1])

        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response({'response': 'success'}, status=status.HTTP_200_OK)

@api_view(['GET',])
@permission_classes([AllowAny])
def isTaskExisted(request):
    task_uid = request.query_params.get('task_uid', None)
    server_name = request.query_params.get('server_name', None)
    server_password = request.query_params.get('server_password', None)
    if None in (task_uid, server_name, server_password):
        return Response({'error': "Parameter errors"}, status=status.HTTP_400_BAD_REQUEST)
    try:
        server = Server.objects.get(username=server_name)
        if not server.check_password(server_password):
            return Response({'error': "Invalid server password"}, status=status.HTTP_400_BAD_REQUEST)
        task = Task.objects.get(task_uid=task_uid, server=server)
    except Server.DoesNotExist:
        return Response({'error': "Invalid server name"}, status=status.HTTP_400_BAD_REQUEST)
    except Task.DoesNotExist:
        return Response(False, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(True, status=status.HTTP_200_OK)
